package com.haolp.sys.service.realm;

import com.haolp.sys.dao.SysMenuDao;
import com.haolp.sys.dao.SysRoleMenuDao;
import com.haolp.sys.dao.SysUserDao;
import com.haolp.sys.dao.SysUserRoleDao;
import com.haolp.sys.pojo.SysUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author 小李
 * @version 1.0
 * @date 2021/10/16 16:15
 */
@Service
public class ShiroUserRealm extends AuthorizingRealm {
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;
    @Autowired
    private SysMenuDao sysMenuDao;

    /**
     * 设置凭证匹配器(此进行加密方法用于告诉认证管理器采用什么加密算法对用户输入的密码)
     * 与用户添加操作使用相同的加密算法
     *
     * @param
     */

    @Override
    public CredentialsMatcher getCredentialsMatcher() {
        //1.构建setCredentialsMatcher对象
        HashedCredentialsMatcher cMatcher = new HashedCredentialsMatcher();
        //2.设置加密算法
        cMatcher.setHashAlgorithmName("MD5");
        //3.设置加密的次数
        cMatcher.setHashIterations(1);
        return cMatcher;
    }

    /**
     * 通过此方法完成认证数据的获取及封装，系统底层会将认证数据传递认证管理器
     * 由认证管理器完成认证操作
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        //1.从token中获取用户信息（前端用户输入的）
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //获取用户名
        String username = upToken.getUsername();
        //2.基于用户名从数据库中查询用户信息
        SysUser sysUser = sysUserDao.findUserByUserName(username);
        //3.判定用户是否存在
        if (sysUser == null)
            throw new UnknownAccountException();
        //4.判定用户是否被禁用
        if (sysUser.getValid() == 0)
            throw new LockedAccountException();
        //5.封装用户信息
        ByteSource salt = ByteSource.Util.bytes(sysUser.getSalt());
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                sysUser,//表示身份信息
                sysUser.getPassword(),//已加密后的密码
                salt,//盐值
                getName());//realName
        //6.将封装后的数据返回给认证管理器
        return info;
    }

    /**
     * 负责获取登录用户权限信息并进行封装
     *
     * @param principals
     * @return
     */

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        //1.获取登录用户身份信息
        SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
        //2.基于用户id查询用户拥有的角色信息
        List<Integer> roleIds = sysUserRoleDao.findRoleIdsByUserId(sysUser.getId());
        if (roleIds == null || roleIds.size() == 0)
            throw new AuthorizationException();
        //3.基于角色id获取菜单id
        List<Integer> menuIds =
                sysRoleMenuDao.findMenuIdsByRoleIds(roleIds.toArray(new Integer[]{}));
        if (menuIds == null || menuIds.size() == 0)
            throw new AuthorizationException();
        //4.基于菜单id查询权限标识
        List<String> permisssions =
                sysMenuDao.findPermisssions(menuIds.toArray(new Integer[]{}));
        if (permisssions == null || permisssions.size() == 0)
            throw new AuthorizationException();
        //5.对权限标识信息封装并返回
        Set<String> set = new HashSet<>();
        for (String pre : permisssions) {
            if (!StringUtils.isEmpty(pre)){
                set.add(pre);
        }
    }
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.setStringPermissions(set);
        return info;
    }
}
